package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.SellerPromotionItemOrderDTO;
import info.batcloud.fanli.core.constants.MessageKeyConstants;
import info.batcloud.fanli.core.entity.SellerPromotionItem;
import info.batcloud.fanli.core.entity.SellerPromotionItemOrder;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.enums.SellerPromotionItemOrderStatus;
import info.batcloud.fanli.core.enums.WalletFlowDetailType;
import info.batcloud.fanli.core.exception.BizException;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.SellerPromotionItemOrderRepository;
import info.batcloud.fanli.core.repository.SellerPromotionItemRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.SellerPromotionItemOrderService;
import info.batcloud.fanli.core.service.SystemSettingService;
import info.batcloud.fanli.core.service.WalletService;
import info.batcloud.fanli.core.settings.SellerPromotionItemSetting;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class SellerPromotionItemOrderServiceImpl implements SellerPromotionItemOrderService {

    @Inject
    private SellerPromotionItemOrderRepository sellerPromotionItemOrderRepository;

    @Inject
    private SellerPromotionItemRepository sellerPromotionItemRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private SystemSettingService systemSettingService;

    @Inject
    private WalletService walletService;

    @Override
    @Transactional
    public RushOrderCreateResult rushCreateOrder(RushOrderCreateParam param) {
        RushOrderCreateResult result = new RushOrderCreateResult();
        //判断商品是否还有抢购余额
        SellerPromotionItem spi = sellerPromotionItemRepository.findOne(param.getSellerPromotionItemId());
        if(spi.getStartTime().after(new Date())) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.SELLER_PROMOTION_ITEM_RUSH_BUY_NOT_START);
            return result;
        }
        SellerPromotionItemSetting sellerPromotionItemSetting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        //读取最近一次该商品的购买订单
        SellerPromotionItemOrder eOrder = sellerPromotionItemOrderRepository
                .findTopByUserIdAndSellerPromotionItemIdOrderByIdDesc(param.getUserId(), param.getSellerPromotionItemId());
        if (eOrder != null) {
            if(eOrder.getStatus() != SellerPromotionItemOrderStatus.CANCELED) {
                //如果订单存在，那么进行判断是否在限制购买周期内
                Date lockedTime = DateUtils.addDays(eOrder.getCreateTime(), sellerPromotionItemSetting.getUserBuyLockedDays());
                if (lockedTime.after(new Date())) {
                    result.setCode(MessageKeyConstants.SELLER_PROMOTION_ITEM_RUSH_BUY_IN_LOCKED, new Object[]{DateFormatUtils.format(lockedTime, "yyyy-MM-dd HH:mm:ss")});
                    result.setSuccess(false);
                    return result;
                }
            }
        }
        int maxCount = sellerPromotionItemOrderRepository.countByUserIdAndStatusIn(param.getUserId(), Arrays.asList(SellerPromotionItemOrderStatus.WAIT_VERIFY,
                SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO));
        if (maxCount >= sellerPromotionItemSetting.getUserRushBuyLimitNum()) {
            //超过最大抢购限制数量
            result.setCode(MessageKeyConstants.SELLER_PROMOTION_ITEM_RUSH_BUY_LIMITED, new Object[]{maxCount, sellerPromotionItemSetting.getUserRushBuyLimitNum()});
            result.setSuccess(false);
            return result;
        }
        if (spi.getRemainNum() <= 0) {
            result.setCode(MessageKeyConstants.SELLER_PROMOTION_ITEM_RUSH_BUY_NO_ENOUGH_NUM);
            result.setSuccess(false);
            return result;
        }
        Date now = new Date();
        spi.setTotalSaleNum(spi.getTotalSaleNum() + 1);
        spi.setRemainNum(spi.getRemainNum() - 1);
        spi.setUpdateTime(now);
        sellerPromotionItemRepository.save(spi);
        SellerPromotionItemOrder order = new SellerPromotionItemOrder();
        order.setCreateTime(now);
        order.setSellerPromotionItemId(spi.getId());
        order.setUserId(param.getUserId());
        order.setStatus(SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO);
        order.setEcomPlat(spi.getEcomPlat());
        order.setRebateFee(0f);
        SellerPromotionItemSetting setting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        Instant endInstant = order.getCreateTime().toInstant().plusSeconds(TimeUnit.DAYS.toSeconds(setting.getAutoRebateTimeoutDays()));
        order.setRebateDeadline(Date.from(endInstant));
        order.setSellerId(spi.getUserId());
        sellerPromotionItemOrderRepository.save(order);
        result.setSuccess(true);
        return result;
    }

    @Override
    /**
     * 当用户最近一次的订单存在，且不是取消的状态，就说明被锁定了。
     * */
    public boolean ifLockedByUserIdAndSellerPromotionItemId(long userId, long sellerPromotionItemId) {
        SellerPromotionItemSetting sellerPromotionItemSetting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        //读取最近一次该商品的购买订单
        SellerPromotionItemOrder eOrder = sellerPromotionItemOrderRepository.findTopByUserIdAndSellerPromotionItemIdOrderByIdDesc(userId, sellerPromotionItemId);
        if (eOrder != null) {
            if (eOrder.getStatus() == SellerPromotionItemOrderStatus.CANCELED) {
                return false;
            }
            //如果订单存在，那么进行判断是否在限制购买周期内
            Date lockedTime = DateUtils.addDays(eOrder.getCreateTime(), sellerPromotionItemSetting.getUserBuyLockedDays());
            return lockedTime.after(new Date());
        }
        return false;
    }

    @Override
    public boolean ifLockedByUserId(long userId) {
        SellerPromotionItemSetting sellerPromotionItemSetting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        int maxCount = sellerPromotionItemOrderRepository.countByUserIdAndStatusIn(userId, Arrays.asList(SellerPromotionItemOrderStatus.WAIT_VERIFY,
                SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO));
        if (maxCount >= sellerPromotionItemSetting.getUserRushBuyLimitNum()) {
            //超过最大抢购限制数量
            return true;
        }
        return false;
    }

    @Override
    public boolean ifFillTradeNoByUserIdAndSellerPromotionItemId(long userId, long sellerPromotionItemId) {
        return sellerPromotionItemOrderRepository.countByUserIdAndSellerPromotionItemIdAndStatus(userId, sellerPromotionItemId, SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO) > 0;
    }

    @Override
    @Transactional
    public void verify(VerifyParam param) {
        SellerPromotionItemOrder order = sellerPromotionItemOrderRepository.findOne(param.getId());
        if (param.getSellerId() != null
                && !order.getSellerId().equals(param.getSellerId())) {
            throw new RuntimeException();
        }
        if (order.getStatus() != SellerPromotionItemOrderStatus.WAIT_VERIFY && order.getStatus() != SellerPromotionItemOrderStatus.WAIT_REBATE
                && order.getStatus() != SellerPromotionItemOrderStatus.VERIFY_FAIL) {
            throw new RuntimeException("当前订单不允许审核!");
        }
        if (param.isSuccess()) {
            order.setStatus(SellerPromotionItemOrderStatus.WAIT_REBATE);
        } else {
            order.setStatus(SellerPromotionItemOrderStatus.VERIFY_FAIL);
        }
        order.setVerifyRemark(param.getVerifyRemark());
        order.setVerifyTime(new Date());
        sellerPromotionItemOrderRepository.save(order);
    }

    @Override
    @Transactional
    public void rebateById(long id) {
        SellerPromotionItemOrder order = sellerPromotionItemOrderRepository.findOne(id);
        rebate(order);
    }

    @Override
    public void rebateBySellerIdAndId(long sellerId, long id) {
        SellerPromotionItemOrder order = sellerPromotionItemOrderRepository.findOne(id);
        Assert.isTrue(sellerId == order.getSellerId(), "");
        rebate(order);
    }

    private void rebate(SellerPromotionItemOrder order) {
        if (order.getStatus() != SellerPromotionItemOrderStatus.WAIT_REBATE) {
            throw new BizException(MessageKeyConstants.SELLER_PROMOTION_ITEM_ORDER_STATUS_NOT_ALLOWED_REBATE);
        }
        order.setStatus(SellerPromotionItemOrderStatus.REBATE_SUCCESS);
        SellerPromotionItem spi = sellerPromotionItemRepository.findOne(order.getSellerPromotionItemId());
        order.setRebateFee(spi.getRebateFee());
        walletService.addMoney(order.getUserId(), spi.getRebateFee(), WalletFlowDetailType.SELLER_PROMOTION_ITEM_REBATE,
                order.getId() + "");
        sellerPromotionItemOrderRepository.save(order);
    }

    @Override
    public void fillTradeNo(long userId, long orderId, String tradeNo) {
        SellerPromotionItemOrder eOrder = sellerPromotionItemOrderRepository.findByUserIdAndId(userId, orderId);
        if (sellerPromotionItemOrderRepository.countByEcomPlatAndOutTradeNo(eOrder.getEcomPlat(), tradeNo) > 0) {
            throw new BizException(MessageKeyConstants.SELLER_PROMOTION_ITEM_ORDER_TRADE_NO_EXISTS);
        }
        if (eOrder.getStatus() != SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO
                && eOrder.getStatus() != SellerPromotionItemOrderStatus.WAIT_VERIFY
                && eOrder.getStatus() != SellerPromotionItemOrderStatus.VERIFY_FAIL) {
            throw new BizException(MessageKeyConstants.SELLER_PROMOTION_ITEM_ORDER_FILL_TRADE_NO_FAIL);
        }
        eOrder.setOutTradeNo(tradeNo);
        eOrder.setStatus(SellerPromotionItemOrderStatus.WAIT_VERIFY);
        SellerPromotionItemSetting setting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        eOrder.setVerifyDeadline(DateUtils.addDays(new Date(), setting.getWaitVerifyTimeoutDays()));
        sellerPromotionItemOrderRepository.save(eOrder);
    }

    @Override
    @Transactional
    public void releaseWaitFillTradeTimeoutOrder() {
        SellerPromotionItemSetting setting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        //找到已经超时的订单
        Date now = new Date();
        Date beforeTime = DateUtils.addMinutes(now, -setting.getOrderHoldTimeoutMinutes());
        List<SellerPromotionItemOrder> orders = sellerPromotionItemOrderRepository
                .findByStatusAndCreateTimeBefore(SellerPromotionItemOrderStatus.WAIT_FILL_TRADE_NO, beforeTime);
        for (SellerPromotionItemOrder order : orders) {
            this.cancelOrder(order);
        }
    }

    @Override
    @Transactional
    public void handleWaitVerifyTimeoutOrder() {
        //找到已经超时的订单
        Date now = new Date();
        List<SellerPromotionItemOrder> orders = sellerPromotionItemOrderRepository
                .findByStatusAndVerifyDeadlineBefore(SellerPromotionItemOrderStatus.WAIT_VERIFY, now);
        for (SellerPromotionItemOrder order : orders) {
            VerifyParam param = new VerifyParam();
            param.setId(order.getId());
            param.setSuccess(true);
            this.verify(param);
        }
    }

    @Override
    @Transactional
    public void handleVerifyFailTimeoutOrder() {
        //找到已经超时的订单
        Date now = new Date();
        List<SellerPromotionItemOrder> orders = sellerPromotionItemOrderRepository
                .findByStatusAndVerifyDeadlineBefore(SellerPromotionItemOrderStatus.VERIFY_FAIL, now);
        for (SellerPromotionItemOrder order : orders) {
            cancelOrder(order);
        }
    }

    @Override
    public void autoRebateTimeoutOrder() {
        SellerPromotionItemSetting setting = systemSettingService.findActiveSetting(SellerPromotionItemSetting.class);
        //找到已经超时的订单
        Date now = new Date();
        Date beforeTime = DateUtils.addDays(now, -setting.getAutoRebateTimeoutDays());
        List<SellerPromotionItemOrder> orders = sellerPromotionItemOrderRepository
                .findByStatusAndCreateTimeBefore(SellerPromotionItemOrderStatus.WAIT_REBATE, beforeTime);
        for (SellerPromotionItemOrder order : orders) {
            rebate(order);
        }
    }

    public void cancelOrder(SellerPromotionItemOrder order) {
        order.setStatus(SellerPromotionItemOrderStatus.CANCELED);
        SellerPromotionItem spi = sellerPromotionItemRepository.findOne(order.getSellerPromotionItemId());
        spi.setTotalSaleNum(spi.getTotalSaleNum() - 1);
        spi.setRemainNum(spi.getRemainNum() + 1);
        sellerPromotionItemRepository.save(spi);
    }

    @Override
    public SellerPromotionItemOrderDTO findByUserIdAndPromotionItemId(long userId, long sellerPromotionItemId) {
        SellerPromotionItemOrder eOrder = sellerPromotionItemOrderRepository.findTopByUserIdAndSellerPromotionItemIdOrderByIdDesc(userId, sellerPromotionItemId);
        if (eOrder == null) {
            return null;
        }
        return toBOList(Arrays.asList(eOrder)).get(0);
    }

    @Override
    public Paging<SellerPromotionItemOrderDTO> search(SearchParam param) {
        Specification<SellerPromotionItemOrder> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("status"), SellerPromotionItemOrderStatus.DELETED));
            }
            if (param.getEcomPlat() != null) {
                expressions.add(cb.equal(root.get("ecomPlat"), param.getEcomPlat()));
            }
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("userId"), param.getUserId()));
            }
            if (param.getSellerId() != null) {
                expressions.add(cb.equal(root.get("sellerId"), param.getSellerId()));
            }
            if (param.getSellerPromotionItemId() != null) {
                expressions.add(cb.equal(root.get("sellerPromotionItemId"), param.getSellerPromotionItemId()));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<SellerPromotionItemOrder> page = sellerPromotionItemOrderRepository.findAll(specification, pageable);
        return PagingHelper.of(toBOList(page.getContent()), page.getTotalElements(), param.getPage(), param.getPageSize());
    }

    private List<SellerPromotionItemOrderDTO> toBOList(List<SellerPromotionItemOrder> list) {
        List<Long> userIds = new ArrayList<>();
        List<Long> sellerPromotionItemIds = new ArrayList<>();
        for (SellerPromotionItemOrder order : list) {
            userIds.add(order.getUserId());
            sellerPromotionItemIds.add(order.getSellerPromotionItemId());
        }
        Iterable<User> userList = userRepository.findByIdIn(userIds);
        Map<Long, User> userMap = new HashMap<>();
        Map<Long, SellerPromotionItem> itemMap = new HashMap<>();
        userList.forEach(u -> userMap.put(u.getId(), u));

        Iterable<SellerPromotionItem> itemIterator = sellerPromotionItemRepository.findAll(sellerPromotionItemIds);
        itemIterator.forEach(i -> itemMap.put(i.getId(), i));
        List<SellerPromotionItemOrderDTO> boList = new ArrayList<>();
        for (SellerPromotionItemOrder order : list) {
            SellerPromotionItemOrderDTO bo = new SellerPromotionItemOrderDTO();
            BeanUtils.copyProperties(order, bo);
            User user = userMap.get(order.getUserId());
            bo.setUserId(user.getId());
            bo.setUserPhone(user.getPhone());
            SellerPromotionItem spi = itemMap.get(order.getSellerPromotionItemId());
            bo.setSellerPromotionItemId(spi.getId());
            bo.setSellerPromotionItemPicUrl(spi.getPicUrl());
            bo.setSellerPromotionItemRebateFee(spi.getRebateFee());
            bo.setSellerPromotionItemOriginPrice(spi.getOriginPrice());
            bo.setSellerPromotionItemTitle(spi.getTitle());
            bo.setSellerPromotionItemShopTitle(spi.getShopTitle());
            bo.setSellerId(order.getSellerId());
            bo.setSellerPromotionItemPrice(spi.getPrice());
            bo.setEcomPlat(spi.getEcomPlat());
            bo.setRebateDeadline(order.getRebateDeadline());
            boList.add(bo);
        }
        return boList;
    }
}
